package com.example.java_gobang.api;

import com.example.java_gobang.game.*;
import com.example.java_gobang.model.User;
import com.example.java_gobang.model.UserMapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;

/**
 * @author hanzishuai
 * @date 2025/03/03 17:27
 * @Description
 */
@Slf4j
@Component
public class GameAPI extends TextWebSocketHandler {

    private ObjectMapper objectMapper = new ObjectMapper();

    @Autowired
    private RoomManger roomManger;

    @Autowired
    private OnlineUserManager onlineUserManager;

    @Autowired
    private UserMapper userMapper;

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        GameReadyResponse resp = new GameReadyResponse();

        // 获取到用户的身份信息
        User user = (User) session.getAttributes().get("user");
        if (user == null) {
            resp.setOk(false);
            resp.setReason("用户未登录！");
            log.warn("用户未登录！");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
            return;
        }

        // 判断当前用户是否已经进入房间
        Room room = roomManger.getRoomByUserId(user.getUserId());
        if (room == null) {
            // 用户未进入房间（未匹配到)
            resp.setOk(false);
            resp.setReason("用户尚未匹配到！");
            log.info("用户未进入房间");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
            return;
        }

        // 用户已经进入房间
        log.info("用户已经进入房间");
        // 判断用户是否多开
        if (onlineUserManager.getFromGameRoom(user.getUserId()) != null
                || onlineUserManager.getFromGameHall(user.getUserId()) != null) {
            // 多开了
            log.warn("用户多开");
            resp.setOk(true);
            resp.setReason("禁止多开游戏页面！");
            resp.setMessage("repeatConnection");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
            return;
        }

        // 设置当前玩家上线
        onlineUserManager.enterGameRoom(user.getUserId(), session);

        synchronized (room) {
            // 把两个玩家加入到游戏房间中
            if (room.getUser1() == null) {
                // 第一个玩家还未进入房间
                // 把连上 webSocket 的玩家作为 user1，加入到房间中
                room.setUser1(user);
                // 先连入房间的玩家作为先手方
                room.setWhiteUser(user.getUserId());
                log.info("玩家作为玩家1 {} 已经准备就绪！", user.getUsername());
                return;
            }
            if (room.getUser2() == null) {
                // 说明第一个玩家已经进入房间，但是第二个玩家还未进入房间
                // 把连上 webSocket 的玩家作为 user2，加入到房间中
                room.setUser2(user);
                log.info("玩家作为玩家2 {} 已经准备就绪！", user.getUsername());

                // 当两个玩家都加入成功之后，让服务器返回 websocket
                // 通知这两个玩家，游戏双方都已经准备好了
                // 通知玩家1
                noticeGameReady(room, room.getUser1(), room.getUser2());

                // 通知玩家2
                noticeGameReady(room, room.getUser2(), room.getUser1());

                return;
            }
        }


        // 此处如果又有一个玩家连接同一个房间
        resp.setOk(false);
        resp.setReason("当前房间已满！");
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));

    }

    private void noticeGameReady(Room room, User thisUser, User thatUser) throws IOException {
        GameReadyResponse resp = new GameReadyResponse();
        resp.setMessage("gameReady");
        resp.setOk(true);
        resp.setReason("");
        resp.setRoomId(room.getRoomId());
        resp.setThisUserId(thisUser.getUserId());
        resp.setThatUserId(thatUser.getUserId());
        resp.setWhiteUser(room.getWhiteUser());

        // 把当前响应的数据传回给玩家
        WebSocketSession webSocketSession = onlineUserManager.getFromGameRoom(thisUser.getUserId());
        webSocketSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        User user = (User) session.getAttributes().get("user");
        if (user == null) {
            log.error("[handleTextMessage]当前玩家尚未登录！");
            return;
        }

        // 根据玩家 id 获取到房间对象
        Room room = roomManger.getRoomByUserId(user.getUserId());
        room.putChess(message.getPayload());

    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        User user = (User) session.getAttributes().get("user");
        if (user == null) {
            return;
        }
        WebSocketSession exitSession = onlineUserManager.getFromGameRoom(user.getUserId());
        if (session == exitSession) {
            onlineUserManager.exitGameRoom(user.getUserId());
        }
        log.error("当前用户 " + user.getUsername() + " 游戏房间连接异常！");

        // 玩家掉线，通知对手获胜
        noticeThatUserWin(user);
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        User user = (User) session.getAttributes().get("user");
        if (user == null) {
            return;
        }
        WebSocketSession exitSession = onlineUserManager.getFromGameRoom(user.getUserId());
        if (session == exitSession) {
            onlineUserManager.exitGameRoom(user.getUserId());
        }
        log.info("当前用户 {} 离开游戏房间！", user.getUsername());

        noticeThatUserWin(user);

    }

    private void noticeThatUserWin(User user) throws IOException {
        // 根据玩家，找到房间
        Room room = roomManger.getRoomByUserId(user.getUserId());
        if (room == null) {
            // 房间已经被释放，也就没有对手了
            log.info("房间已经被释放,无需通知对手");
            return;
        }
        // 根据房间，找到对手
        User thatUser = (user == room.getUser1()) ? room.getUser2() : room.getUser1();
        WebSocketSession webSocketSession = onlineUserManager.getFromGameRoom(thatUser.getUserId());
        if (webSocketSession == null) {
            // 对手也掉线了！
            log.info("对手也掉线了，无需通知！");
            return;
        }

        GameResponse resp = new GameResponse();
        resp.setMessage("putChess");
        resp.setUserId(thatUser.getUserId());
        resp.setWinner(thatUser.getUserId());
        webSocketSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));

        // 更新玩家的分数信息
        int winUserId = thatUser.getUserId();
        int loseUserId = user.getUserId();
        userMapper.userWin(winUserId);
        userMapper.userLose(loseUserId);

        // 释放房间对象
        roomManger.remove(room.getRoomId(), room.getUser1().getUserId(), room.getUser2().getUserId());
    }
}
